package supertuxwar;

import java.util.Random;

import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;

import supertuxwar.powerup.Mine;
import supertuxwar.powerup.Pistol;
import supertuxwar.powerup.Powerup;

/**
 * Simple platform single state to render the tile map and the entities, update
 * the physical world and allow player input
 * 
 * @author kevin
 */
public class InGameState extends BasicGameState {
	/** The unique ID given to the state */
	private static final int ID = 1;
	
	/** The environment in which the physics is taking place */
	private Environment env = TileEnvironment.getInstance();
	/** The player that is being controlled */
	private Actor player1;
	private Actor player2;
	private int deathTimer = 0;
	private int player1DeathCount = 0;
	private int player2DeathCount = 0;
	/** The background image to be displayed */
	private Image background;
	
	/** The amount of time passed since last control check */
	private int totalDelta;
	/** The interval to check the controls at */
	private int controlInterval = 50;
	/** True if we're showing the bounds of the environment's shapes */
	private boolean showBounds = false;
	
	private Powerup powerup;
	private int powerupTimer;
	private Random rand = new Random();
	
	/**
	 * @see org.newdawn.slick.state.BasicGameState#getID()
	 */
	public int getID() {
		return ID;
	}

	/**
	 * @see org.newdawn.slick.state.GameState#init(org.newdawn.slick.GameContainer, org.newdawn.slick.state.StateBasedGame)
	 */
	public void init(GameContainer container, StateBasedGame game)
			throws SlickException {
		background = new Image("res/background.jpg");
		
		restart();
	}

	/**
	 * Restart the demo
	 * 
	 * @throws SlickException Indicates a failure to load resources
	 */
	private void restart() throws SlickException {
		deathTimer = 0;
		if(player1 != null && player1.death()) {
			player1DeathCount++;
		}
		if(player2 != null && player2.death()) {
			player2DeathCount++;
		}
		
		((TileEnvironment)env).restart();
		
		player1 = new SuperTux(48,450,1f,32, true);
		env.addEntity(player1);
		player2 = new SuperTux(752,450,1f,32, false);
		env.addEntity(player2);
		
		env.addEntity(new Crate(300,100, 60,60,10));
		env.addEntity(new Crate(550,40, 46,46,5));
		env.addEntity(new Crate(555,-10, 46,46,5));
		env.addEntity(new Crate(545,100, 46,46,5));
		
	}
	
	/**
	 * @see org.newdawn.slick.state.GameState#render(org.newdawn.slick.GameContainer, org.newdawn.slick.state.StateBasedGame, org.newdawn.slick.Graphics)
	 */
	public void render(GameContainer container, StateBasedGame game, Graphics g)
			throws SlickException {
		background.draw(0, 0);
		env.render(g);
		if (showBounds) {
			env.renderBounds(g);
		}
		
		g.drawString("DEATH " + player1DeathCount, 20, 0);
		g.drawString("DEATH " + player2DeathCount, 720, 0);
		
		if(player1.getPowerup() != null) {
			Image image = player1.getPowerup().getImage();
			
			image.drawCentered(32, 32);
		}
		
		if(player2.getPowerup() != null) {
			Image image = player2.getPowerup().getImage();
			
			image.drawCentered(768, 32);
		}
	}

	/**
	 * Draw an clear string centred horizontally
	 * 
	 * @param g The graphics context on which to draw the string
	 * @param str The string to draw
	 * @param y The vertical location to draw at
	 */
	private void drawString(Graphics g, String str, int y) {
		int x = (800 - g.getFont().getWidth(str)) / 2;
		
		g.setColor(Color.black);
		g.drawString(str, x+1,y+1);
		g.setColor(Color.white);
		g.drawString(str, x,y);
		
	}
	
	/**
	 * @see org.newdawn.slick.state.GameState#update(org.newdawn.slick.GameContainer, org.newdawn.slick.state.StateBasedGame, int)
	 */
	public void update(GameContainer container, StateBasedGame game, int delta)
			throws SlickException {
		
		if(player1.death() || player2.death()) {
			deathTimer += delta;
			if(deathTimer > 2100) {
				restart();
			}
		}
		
		// Powerup
		powerupTimer += delta;
		if(powerupTimer > 10000) {
			if(powerup != null) {
				env.removeEntity(powerup);
			}
			int r = rand.nextInt(2);
			System.out.println("r=" + r);
			switch(r) {
			case 0:
				powerup = new Mine(400, 32);
				break;
			case 1:
				powerup = new Pistol(400, 32);
				break;
			}
			env.addEntity(powerup);
			
			powerupTimer = 0;
		}
		
		Input input = container.getInput();
		
		// restart and bounds toggling
		if (input.isKeyPressed(Input.KEY_R)) {
			restart();
			return;
		}
		if (input.isKeyPressed(Input.KEY_B)) {
			showBounds = !showBounds;
		}

		// the forces applied for different actions. The move force is applied over and
		// over so is reasonably small. The jump force is a one shot deal and so is reasonably
		// big
		float moveForce = 100;
		float jumpForce = 20000;
		
		totalDelta += delta;
		
		// setup the player's moving flag, this control the animation
		player1.setMoving(false);
		if (input.isKeyDown(Input.KEY_LEFT)) {
			player1.setMoving(true);
		}
		if (input.isKeyDown(Input.KEY_RIGHT)) {
			player1.setMoving(true);
		}
		
		player2.setMoving(false);
		if (input.isKeyDown(Input.KEY_A)) {
			player2.setMoving(true);
		}
		if (input.isKeyDown(Input.KEY_D)) {
			player2.setMoving(true);
		}		
		
		// only check controls at set interval. If we don't do this different
		// frame rates will effect how the controls are interpreted
		if (totalDelta > controlInterval) {
			totalDelta -= controlInterval;
			/**************************** Player1 ******************************/
			if (input.isKeyDown(Input.KEY_LEFT)) {
				player1.applyForce(-moveForce, 0);
			}
			if (input.isKeyDown(Input.KEY_RIGHT)) {
				player1.applyForce(moveForce, 0);
			}
			if (player1.onGround()) {
				if (input.isKeyPressed(Input.KEY_UP)) {
					if (player1.facingRight()) {
						player1.applyForce(0, -jumpForce);
					} else {
						player1.applyForce(0, -jumpForce);
					}
				}
			}

			// 轻按UP键会导致较小的跳跃幅度，按住UP键会导致较大的跳跃幅度
			if (!input.isKeyDown(Input.KEY_UP)) {
				if (player1.jumping()) {
					player1.setVelocity(player1.getVelX(), player1.getVelY() * 0.85f);
				}
			}
			
			if(input.isKeyDown(Input.KEY_DOWN)) {
				if(player1.getPowerup() != null) {
					player1.getPowerup().doAction();
					player1.setPowerup(null);
				}
			}
			
			/**************************** Player2 ******************************/
			if (input.isKeyDown(Input.KEY_A)) {
				player2.applyForce(-moveForce, 0);
			}
			if (input.isKeyDown(Input.KEY_D)) {
				player2.applyForce(moveForce, 0);
			}
			if (player2.onGround()) {
				if (input.isKeyPressed(Input.KEY_W)) {
					if (player2.facingRight()) {
						player2.applyForce(0, -jumpForce);
					} else {
						player2.applyForce(0, -jumpForce);
					}
				}
			}

			// 轻按W键会导致较小的跳跃幅度，按住W键会导致较大的跳跃幅度
			if (!input.isKeyDown(Input.KEY_W)) {
				if (player2.jumping()) {
					player2.setVelocity(player2.getVelX(), player2.getVelY() * 0.85f);
				}
			}			
			
			if(input.isKeyDown(Input.KEY_S)) {
				if(player2.getPowerup() != null) {
					player2.getPowerup().doAction();
					player2.setPowerup(null);
				}
			}
		}
		
		// update the environemnt and hence the physics world
		env.update(delta);
	}

}
